#pragma once
#include<iostream>
#include<string>
#include <jsoncpp/json/json.h>
#include<mutex>

#include"view.hpp"
#include"model.hpp"
#include"common.hpp"
#include"../CommonComponent/Log.hpp"
#include"../ThirdLibrary/httplib.h"

namespace ns_control{
    //负载均衡功能
    std::string myconfig="./compileMachineConfig";
    std::string firstHtml="./wwwhtml/index.html";
    std::string listHtml="./wwwhtml/questionlist.html";
    std::string detailHtml="./wwwhtml/questiondetail.html";
    //描述一台编译机器
    class compileMachine{
    public:
        compileMachine(std::string ip,int port,mutex* m)
            :_ip(ip),
            _port(port),
            _load(0),
            _mutex(m)
        {}

        void AddLoad(){
            _mutex->lock();
            ++_load;
            _mutex->unlock();
        }

        void SubLoad(){
            _mutex->lock();
            --_load;
            _mutex->unlock();
        }

        void SetLoadZero(){
            _mutex->lock();
            --_load=0;
            _mutex->unlock();
        }

        int GetLoad(){
            return _load;
        }

        const std::string& GetIp(){
            return _ip;
        }

        const int GetPort(){
            return _port;
        }

        mutex* GetMutex(){
            return _mutex;
        }

        ~compileMachine()
        {}
    private:
        std::string _ip;    //编译主机的ip
        int _port;          //编译主机的端口
        int _load;          //当前编译机器载荷
        mutex* _mutex;      //互斥锁
    };

    //负载均衡
    class loadBalance{
    public:
        loadBalance(){
            //从配置文件compileMachineConfig加载所有编译主机的ip和端口
            std::ifstream in(myconfig);
            if(!in.is_open()){
                Log(FATAL,"can not load %s\n",myconfig.c_str());
                exit(-1);
            }
            std::string tmp;
            while(std::getline(in,tmp))
            {
                if(0==tmp.size()){
                    continue;
                }
                mutex* pm=new mutex;
                if(nullptr==pm){
                    Log(ERROR,"new mutex error\n");
                    continue;
                }

                std::vector<std::string> host;
                ns_mvccom::StringSplit(tmp,host,' ');
                if(2!=host.size()){
                    Log(ERROR,"split error,check the file %s\n",myconfig.c_str());
                    continue;
                }

                _onlineMachines.push_back(_machines.size());
                _machines.push_back(compileMachine(host[0],std::stoi(host[1]),pm));
                Log(INFO,"load machine ip[%s] port[%s] successfully\n",host[0].c_str(),host[1].c_str());
            }
            in.close();
            if(0==_machines.size()){
                Log(FATAL,"no machine load successfully\n");
                exit(-1);
            }
        }

        void OfflineMachine(int id){
            if(id<0||id>=_machines.size()){
                return;
            }
            _mutex.lock();
            auto it=_onlineMachines.begin();
            while(it!=_onlineMachines.end()){
                if(*it==id){
                    break;
                }
            }
            if(_onlineMachines.end()==it){
                _mutex.unlock();
                return;
            }
            _offlineMachines.push_back(*it);
            _onlineMachines.erase(it);
            _mutex.unlock();
        }

        void OnlineMachine(){
            //为了简单，我们选择当所有主机离时，就认为所有离线已经修复，直接将所有主机上线
            _mutex.lock();
            int i=_offlineMachines.size();
            while(i--){
                _onlineMachines.push_back(_offlineMachines.back());
                _offlineMachines.pop_back();
            }
            _mutex.unlock();
        }

        compileMachine* GetMachile(int id){
            if(id<0||id>=_machines.size()){
                return nullptr;
            }

            return &_machines[id];
        }

        //负载均衡核心函数，由它智能选择一台负载最少的编译主机
        int SmartChoice(){
            _mutex.lock();
            if(0==_onlineMachines.size()){
                _mutex.unlock();
                return -1;
            }
            int index=0;
            int cur=1;
            for(;cur<_onlineMachines.size();++cur){
                if(_machines[_offlineMachines[cur]].GetLoad()<_machines[_offlineMachines[index]].GetLoad()){
                    index=cur;
                }
            }

            _mutex.unlock();
            return _onlineMachines[index];
        }


        ~loadBalance()
        {
            for(auto& e:_machines){
                free(e.GetMutex());
            }
        }
    private:
        std::vector<compileMachine> _machines;  //所有主机
        std::vector<int> _offlineMachines;      //离线主机
        std::vector<int> _onlineMachines;       //在线主机
        mutex _mutex;    
    };

    static loadBalance lb;

    void CommitCode(std::string& in_json,std::string& html){
        //1.获取代码测试需要的数据
        Json::Value root;
        Json::Reader reader;
        reader.parse(in_json,root);

        ns_mvccom::questionTest qt;
        ns_module::GetQuestionTest(root["id"].asString(),qt);

        //2.拼接代码并构建json串
        std::string code=root["code"].asString();
        code+="\n";
        code+=qt.testcode;
        Json::Value r;
        r["input"]=root["input"];
        r["code"]=code;
        r["timelimit"]=qt.timelimit;
        r["spacelimit"]=qt.spacelimit;
        std::string commit_json=r.toStyledString();

        //3.负载均衡地选择一台后台的编译运行服务
        while(true){
            int id = lb.SmartChoice();
            if(-1==id){
                Log(FATAL,"all machines has offline!!!\n");
                return;
            }
            compileMachine* m=lb.GetMachile(id);
            //4.交付后台编译并获取执行结果
            m->AddLoad();
            httplib::Client client(m->GetIp().c_str(),m->GetPort());
            auto ret=client.Post("/compileAndRun",commit_json,"application/json;charset=utf-8");
            m->SubLoad();

            //5.分析执行结果构建对应网页
            if(200==ret->status){
                Json::Value result;
                Json::Reader read;
                read.parse(ret->body,result);
                if(-2==result["status"].asInt()){
                    //说明编译时发生了内部错误，不需要用户知道
                    return;
                }
                //构建网页？？？？？？？？？？？？？？？？？？？？？


                return;
            }
            else{
                //执行到这里我们认为主机掉线了
                Log(ERROR,"machine ip[%s] port[%d] offline\n",m->GetIp(),m->GetPort());
                lb.OfflineMachine(id);
                //继续选择下一台主机
            }
            
        }
    }

    void GetList(std::string& html){
        //获取数据
        std::vector<ns_mvccom::questionList> qlists;
        ns_module::GetQuestionList(qlists);

        //构建网页？？？？？？？？？？？？？？？？？？？？？？？？？？？？？？？？
        for(auto& e:qlists){
            printf("%d %s %s\n",e.id,e.title.c_str(),e.level.c_str());
        }

    }

    void GetDetails(std::string& in_json,std::string& html){
        //获取数据
        Json::Value root;
        Json::Reader reader;
        if(!reader.parse(in_json,root)){
            Log(ERROR,"parse error\n");
            return;
        }

        ns_mvccom::questionDetail q;
        ns_module::GetQuestionDetail(root["id"].asString(),q);
        //构建网页？？？？？？？？？？？？？？？？？？？？？？？？？？？？？？？？？？


    }

    void GetFirst(std::string& html){
        if(ns_mvccom::Read(firstHtml,html)){
            Log(INFO,"get firstHtml successfully\n");
            return;
        }
        Log(ERROR,"get firstHtml error\n");
    }

    void start(std::string host,int port,int socket_flags=0){
        httplib::Server svr;

        /*
            如果post方法的URL的请求中的资源路径为/，则是用户想要进入网站，我们返回网站首页即可
        */
        svr.Get("/", [](const httplib::Request &req, httplib::Response &resp){
            std::string html;
            GetFirst(html);
            if(!html.empty()){
                resp.set_content(html, "text/html;charset=utf-8");
            }
        });
        
        /*
            如果post方法的URL的请求中的资源路径为/commit，则是用户进行代码提交代码
            正文部分为json串，字段包括：
            "id" : "..."    --题目编号
            "code" : "..."  --提交的代码
            "input" : "..." --输入
        */
        svr.Post("/commit", [](const httplib::Request &req, httplib::Response &resp){
            std::string in_json = req.body;
            std::string html;
            if(!in_json.empty()){
                CommitCode(in_json,html);
                if(!html.empty()){
                    resp.set_content(html, "text/html;charset=utf-8");
                }
            }
        });


        /*
            如果Get方法的URL的请求中的资源路径为/questionlist，则是用户请求题目列表
            不需要正文部分
        */
        svr.Get("/questionlist", [](const httplib::Request &req, httplib::Response &resp){
            std::string html;
            GetList(html);
            if(!html.empty()){
                resp.set_content(html, "text/html;charset=utf-8");
            }
        });

        /*
            如果Get方法的URL的请求中的资源路径为/questiondetails，则是用户查看某个具体的题目
            正文部分为json串，字段包括：
            "id" : "..."    --题目编号
        */
        svr.Get("/questiondetails", [](const httplib::Request &req, httplib::Response &resp){
            std::string in_json = req.body;
            std::string html;
            if(!in_json.empty()){
                GetDetails(in_json,html);
                if(!html.empty()){
                    resp.set_content(html, "text/html;charset=utf-8");
                }
            }
        });

        svr.listen(host.c_str(),port,socket_flags); //启动http服务
    }
}